package com.bjpowernode.configs;


import com.alibaba.fastjson.JSON;
import com.bjpowernode.model.Result;
import com.bjpowernode.constant.LoginConstant;
import com.bjpowernode.filter.TokenCheckFilter;
import com.bjpowernode.service.LoginLogService;
import com.bjpowernode.service.impl.UserDetailsServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;
import java.io.PrintWriter;
import java.time.Duration;
import java.util.UUID;

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private UserDetailsServiceImpl userDetailsServiceImpl;
    @Resource
    private TokenCheckFilter tokenCheckFilter;
    @Resource
    private LoginLogService loginLogService;

    /**
     * 认证配置
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsServiceImpl);
    }

    /**
     * 配置静态资源放行处理,忽略mvc规则等配置
     *
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        // 放行配置
        web.ignoring()
                .antMatchers(
                        // 放行swagger-ui
                        "/swagger-ui/*",
                        "/swagger-resources/**",
                        "/v2/api-docs",
                        "/v3/api-docs",
                        "/webjars/**");
    }

    /**
     * 网络请求配置
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 注入进来TokenCheckFilter
        http.addFilterBefore(tokenCheckFilter, UsernamePasswordAuthenticationFilter.class);
        http.csrf().disable(); // 关闭跨域请求伪造
        http.formLogin()
                .loginProcessingUrl("/doLogin")
                // 登录成功处理器
                .successHandler(successHandler())
                // 登录失败处理器
                .failureHandler(failureHandler())
                .permitAll();
        // 放行的配置
        http.authorizeRequests()
                .antMatchers(LoginConstant.ALLOW_URL)
                .permitAll();
        // 其它配置必须登录才能访问
        http.authorizeRequests()
                .anyRequest()
                .authenticated();
    }

    /**
     * 登录成功处理器
     * 1.设置编码
     * 2.使用uuid生成一个token
     * 3.将token存入到redis
     * 4.将token写出去
     *
     * @return
     */
    @Bean
    public AuthenticationSuccessHandler successHandler() {
        return (request, response, authentication) -> {
            // 设置编码格式
            response.setContentType("application/json;charset=utf-8");
            // 生成token
            String token = UUID.randomUUID().toString().replaceAll("-", "");
            // 存入redis保存时间为7天
            stringRedisTemplate.opsForValue().set(LoginConstant.LOGIN_PREFIX + token,
                    JSON.toJSONString(authentication),
                    Duration.ofDays(7));
            // 登录时需要记录日志
            loginLogService.addLog(1,MyThreadLocal.threadLocal.get());
            // 写出token
            Result<String> result = Result.success("ok", token);
            ObjectMapper objectMapper = new ObjectMapper();
            String s = objectMapper.writeValueAsString(result);
            PrintWriter writer = response.getWriter();
            writer.write(s);
            writer.flush();
            writer.close();
        };
    }

    /**
     * 登录失败处理器
     *
     * @return
     */
    @Bean
    public AuthenticationFailureHandler failureHandler() {
        return (request, response, exception) -> {
            // 设置编码格式
            response.setContentType("application/json;charset=utf-8");
            // 登录失败也需要记录日志
            loginLogService.addLog(0,MyThreadLocal.threadLocal.get());
            Result<String> result = new Result<>(401, "用户名或密码错误", null);
            // 写出token
            ObjectMapper objectMapper = new ObjectMapper();
            String s = objectMapper.writeValueAsString(result);
            PrintWriter writer = response.getWriter();
            writer.write(s);
            writer.flush();
            writer.close();
        };
    }

    /***
     * 创建密码比较器
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

}
